/* **********************************************************
 * Copyright 2010 Dimitar Vangelovski
 *
 * This file is part of the Gens3 3D Engine.
 *
 * The Gens3 3D Engine is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * The Gens3 3D Engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the Gens3 3D Engine; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 * **********************************************************/


#include "App.h"
#include "LoaderASE.h"
#include "Material.h"
#include "SubMaterial.h"
#include "MapDiffuse.h"
#include "GeomObject.h"
#include "Mesh.h"
#include "MeshTVertList.h"
#include "MeshTFaceList.h"
#include "LightObject.h"
#include "Scene.h"
#include "AnimObject.h"

CLoaderASE::CLoaderASE()
{
	m_pFileName = "";
	m_theFile = NULL;
	m_theBuffer = NULL;

	m_pScene = NULL;
	m_pMaterial = NULL;
	m_pMapDiffuse = NULL;
	m_pSubMaterial = NULL;

	m_pGeomObject = NULL;
	m_pMesh = NULL;
	m_pMeshTVertList = NULL;
	m_pMeshTFaceList = NULL;

	m_pLightObject = NULL;

	m_pAnimObject = NULL;

	m_iMaterialCount = 0;
	m_iGeomObjectCount = 0;
	m_iLightObjectCount = 0;
	m_iCurrentObjectIndicator = CLoaderASE::eUndefinedInd;
	m_iAnimationObjectIndicator = CLoaderASE::eAnimUnknownTag;
}

CLoaderASE::~CLoaderASE()
{
}

void CLoaderASE::OpenFile(const char * fileName)
{
	m_pFileName = fileName;

	// Open the file
	//if (::fopen_s(&m_theFile, fileName, "rtS") != 0)

	m_theFile = fopen(fileName, "rtS");

	if (!m_theFile)
	{
		CApp::WriteToLog("The file was not opened\n");
		exit(1);
	}

	// Set a stream buffer
	m_theBuffer = new char[1024];
	::setvbuf(m_theFile, m_theBuffer, _IOFBF, sizeof(m_theBuffer));
}

void CLoaderASE::CloseFile()
{
	// Close the file
	if (::fclose(m_theFile) != 0)
	{
		CApp::WriteToLog("The file was not closed !!!\n");
	}
}

int CLoaderASE::ReadLine(std::string &strLine)
{
	const unsigned int iMaxChars = 512;
	char line[iMaxChars];

	// Read a line from file until the first newline character
	if (::fgets(line, iMaxChars, m_theFile) == NULL)
	{
		return -1;
	}

	// Convert the char array to std::string
	strLine = std::string(line);

	return 0;
}

int CLoaderASE::CheckTags(std::string &strLine)
{
	int iPos = 0;

	// Search the line for known tags
	for (unsigned int i = 0; i < g_iGeneralASETagsCount; i++)
	{
		iPos = strLine.find(g_strGeneralASETags[i]);
		if (iPos != std::string::npos)
		{
			// Erase the tag from the string
			strLine = strLine.erase(iPos, g_strGeneralASETags[i].length());
			return eGeneralASETags(i);
		}
	}

	return -1; // Known tag not found
}

int CLoaderASE::CheckTagsAnimate(std::string &strLine)
{
	int iPos = 0;

	// Search the line for known tags
	for (unsigned int i = 0; i < g_iAnimateTagsCount; i++)
	{
		iPos = strLine.find(g_strAnimateTags[i]);
		if (iPos != std::string::npos)
		{
			return eAnimateTags(i);
		}
	}

	return -1; // Known tag not found
}

bool CLoaderASE::ParseAnimationParms(std::string strLine)
{
	///////////////////////////////////////////////////////////////////////////
	// check for animation tags

	std::vector<std::string> strTokens;

	//m_iAnimationObjectIndicator = CLoaderASE::eAnimUnknownTag;

	int iTemp = CheckTagsAnimate(strLine);
	if (iTemp == -1) return false;

	strTokens.clear();
	ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\" ", strTokens);

	m_pAnimObject = new CAnimObject();

	// set animation params
	switch (iTemp)
	{
		case CLoaderASE::eAnimRotate:
			{
				m_pAnimObject->ax = atof(strTokens[3].c_str());
				m_pAnimObject->ay = atof(strTokens[4].c_str());
				m_pAnimObject->az = atof(strTokens[2].c_str());
				m_pAnimObject->m_strRotCenter = strTokens[5];
				m_iAnimationObjectIndicator = CLoaderASE::eAnimRotate;
				break;
			}
		case CLoaderASE::eAnimTranslate:
			{
				m_pAnimObject->tx = atof(strTokens[3].c_str());
				m_pAnimObject->ty = atof(strTokens[4].c_str());
				m_pAnimObject->tz = atof(strTokens[2].c_str());
				m_pAnimObject->m_fTrSpeed = atof(strTokens[5].c_str());
				m_pAnimObject->m_fTrRange = atof(strTokens[6].c_str());
				m_iAnimationObjectIndicator = CLoaderASE::eAnimTranslate;
				break;
			}
	}

	return true;
}

void CLoaderASE::ParseLine(char * pString, const char * pDelimiter, std::vector<std::string> &strTokens)
{
	char *	pToken;
	//char *	pNextToken = '\0';

	//std::cout << "ParseLine() pString: " << pString << std::endl;

	//pToken = ::strtok_s(pString, pDelimiter, &pNextToken);
	pToken = strtok(pString, pDelimiter);

	while (pToken != NULL)
	{
		strTokens.push_back(std::string(pToken));
		//pToken = ::strtok_s(NULL, pDelimiter, &pNextToken);
		pToken = strtok(NULL, pDelimiter);
	}
}

void CLoaderASE::ParseFile()
{
	char cTemp;
	int iTemp, iCount;
	float fTemp, fTemp1, fTemp2, fTemp3;
	std::vector<std::string> strTokens, strTokens1, strTokens2, strTokens3, strTokens4;
	std::string strLine, strObjNameTemp;

    try
    {
        // Read every line in the file until EOF
        while (!::feof(m_theFile))
        {
            ReadLine(strLine);

            switch (CheckTags(strLine))
            {
                case CLoaderASE::eUnknownTag:
                    {
                        //std::cout << "A known tag has not been found in this line!" << std::endl;
                        break;
                    }
                case CLoaderASE::eSceneFilename:
                    {
                        strTokens.clear();
                        ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\"", strTokens);

                        m_pScene = new CScene();
                        m_pScene->SetSceneFilename(strTokens[0]);
                        break;
                    }

                ///////////////////////////////////////////////////////////////////////////////
                // Materials parsing

                case CLoaderASE::eMaterialList:
                    {
                        // Not needed for now
                        break;
                    }
                case CLoaderASE::eMaterialCount:
                    {
                        ::sscanf(strLine.c_str(), "%i", &m_iMaterialCount);
                        m_pScene->SetMaterialCount(m_iMaterialCount);
                        break;
                    }
                case CLoaderASE::eMaterial:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);

                        m_pMaterial = new CMaterial();
                        m_pMaterial->SetMaterialIndex(iTemp);

                        // always create a default diffuse map just in case there is not any defined in the .ase file
                        m_pMapDiffuse = new CMapDiffuse();
                        m_pMaterial->SetMapDiffuse(m_pMapDiffuse);

                        m_theStack.push('{'); // a Material nested structure begins, so insert '{' in the stack
                        m_iCurrentObjectIndicator = CLoaderASE::eMaterialInd;
                        break;
                    }
                case CLoaderASE::eMaterialName:
                    {
                        strTokens.clear();
                        ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\"", strTokens);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialName(strTokens[0]);
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialName(strTokens[0]);
                        }
                        break;
                    }
                case CLoaderASE::eMaterialAmbient:
                    {
                        ::sscanf(strLine.c_str(), "%f%f%f", &fTemp1, &fTemp2, &fTemp3);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialAmbient(CColorRGB(fTemp1, fTemp2, fTemp3));
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialAmbient(CColorRGB(fTemp1, fTemp2, fTemp3));
                        }
                        break;
                    }
                case CLoaderASE::eMaterialDiffuse:
                    {
                        ::sscanf(strLine.c_str(), "%f%f%f", &fTemp1, &fTemp2, &fTemp3);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialDiffuse(CColorRGB(fTemp1, fTemp2, fTemp3));
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialDiffuse(CColorRGB(fTemp1, fTemp2, fTemp3));
                        }
                        break;
                    }
                case CLoaderASE::eMaterialSpecular:
                    {
                        ::sscanf(strLine.c_str(), "%f%f%f", &fTemp1, &fTemp2, &fTemp3);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialSpecular(CColorRGB(fTemp1, fTemp2, fTemp3));
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialSpecular(CColorRGB(fTemp1, fTemp2, fTemp3));
                        }
                        break;
                    }
                case CLoaderASE::eMaterialShine:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialShine(fTemp);
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialShine(fTemp);
                        }
                        break;
                    }
                case CLoaderASE::eMaterialShineStrength:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialShineStrength(fTemp);
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialShineStrength(fTemp);
                        }
                        break;
                    }
                case CLoaderASE::eMaterialTransparency:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialTransparency(fTemp);
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialTransparency(fTemp);
                        }
                        break;
                    }
                case CLoaderASE::eMaterialSelfIllum:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);

                        if (m_iCurrentObjectIndicator == CLoaderASE::eSubMaterialInd)
                        {
                            if (m_pSubMaterial != NULL)
                                m_pSubMaterial->SetMaterialSelfIllum(fTemp);
                        }
                        else
                        {
                            if (m_pMaterial != NULL)
                                m_pMaterial->SetMaterialSelfIllum(fTemp);
                        }
                        break;
                    }
                case CLoaderASE::eMapDiffuse:
                    {
                        m_theStack.push('{');
                        break;
                    }
                case CLoaderASE::eNumSubMtls:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pMaterial->SetNumSubMtls(iTemp);
                        break;
                    }
                case CLoaderASE::eMapName:
                    {
                        strTokens.clear();
                        ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\"", strTokens);
                        if (m_pMapDiffuse != NULL)
                        {
                            m_pMapDiffuse->SetMapName(strTokens[0]);
                        }
                        break;
                    }
                case CLoaderASE::eBitmap:
                    {
                        strTokens.clear();

                        //std::cout << "CLoaderASE::eBitmap strLine.c_str(): " << strLine.c_str() << std::endl;

                        ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\"\\", strTokens);

                        //for (int iii = 0; iii < strTokens.size(); iii++)
                        //{
                        //	std::cout << "CLoaderASE::eBitmap strTokens(i): " << strTokens[iii] << std::endl;
                        //}

                        if (m_pMapDiffuse != NULL)
                        {
                            if (strTokens[strTokens.size() - 1].length() < 4)
                                m_pMapDiffuse->SetBitmap(strTokens[strTokens.size() - 2]);
                            else
                                m_pMapDiffuse->SetBitmap(strTokens[strTokens.size() - 1]);
                        }
                        break;
                    }
                case CLoaderASE::eUVW_U_Offset:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);
                        m_pMapDiffuse->SetUVW_U_Offset(fTemp);
                        break;
                    }
                case CLoaderASE::eUVW_V_Offset:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);
                        m_pMapDiffuse->SetUVW_V_Offset(fTemp);
                        break;
                    }
                case CLoaderASE::eUVW_U_Tiling:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);
                        m_pMapDiffuse->SetUVW_U_Tiling(fTemp);
                        break;
                    }
                case CLoaderASE::eUVW_V_Tiling:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);
                        m_pMapDiffuse->SetUVW_V_Tiling(fTemp);
                        break;
                    }
                case CLoaderASE::eSubMaterial:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);

                        m_pSubMaterial = new CSubMaterial();
                        m_pSubMaterial->SetSubMaterialIndex(iTemp);

                        m_theStack.push('{');
                        m_iCurrentObjectIndicator = CLoaderASE::eSubMaterialInd;

                        if (m_pMaterial != NULL && m_pMapDiffuse != NULL)
                        {
                            m_pMaterial->SetMapDiffuse(m_pMapDiffuse); // we got to a submaterial(s), so set the MapDiffuse for the main material
                        }
                        break;
                    }

                //////////////////////////////////////////////////////////////////////////////////
                // GeomObjects parsing

                case CLoaderASE::eGeomObject:
                    {
                        m_pGeomObject = new CGeomObject();
                        m_theStack.push('{');
                        m_iCurrentObjectIndicator = CLoaderASE::eGeomObjectInd;
                        m_iAnimationObjectIndicator = CLoaderASE::eAnimUnknownTag;
                        break;
                    }
                case CLoaderASE::eNodeName:
                    {
                        if (m_iCurrentObjectIndicator == CLoaderASE::eUndefinedInd) // unknown object => skip processing
                            continue;

                        strTokens.clear();
                        ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\"", strTokens);

                        if (strObjNameTemp.compare(strTokens[0]) == 0) // if an object with that name nas already been loaded => skip processing
                            continue;

                        strObjNameTemp = strTokens[0];

                        bool bHasAnimParams = ParseAnimationParms(strTokens[0]);

                        /////////////////////////////////////////////////////////////////////////////
                        // parse the object name

                        switch (m_iCurrentObjectIndicator)
                        {
                            case CLoaderASE::eGeomObjectInd:
                                {
                                    if (m_pGeomObject != NULL)
                                    {
                                        m_pGeomObject->SetNodeName(strTokens[0]);
                                    }
                                    if (m_pAnimObject != NULL && bHasAnimParams)
                                    {
                                        m_pAnimObject->SetObjectType(CAnimObject::eGeomObject);
                                    }
                                    break;
                                }
                            case CLoaderASE::eLightObjectInd:
                                {
                                    if (m_pLightObject != NULL)
                                    {
                                        m_pLightObject->SetNodeName(strTokens[0]);
                                    }
                                    if (m_pAnimObject != NULL && bHasAnimParams)
                                    {
                                        iTemp = strTokens[0].find("RotCenter");
                                        if (!(iTemp != std::string::npos && strTokens[0].length() < 16))
                                        {
                                            m_pAnimObject->SetObjectType(CAnimObject::eLightObject);
                                        }
                                    }
                                    break;
                                }
                        }
                        break;
                    }
                case CLoaderASE::eNodeParent:
                    {
                        if (m_iCurrentObjectIndicator == CLoaderASE::eUndefinedInd) // unknown object => skip processing
                            continue;

                        strTokens.clear();
                        ParseLine(const_cast<char *>(strLine.c_str()), "\t\n\"", strTokens);

                        if (strObjNameTemp.compare(strTokens[0]) == 0) // if an object with that name nas already been loaded => skip processing
                            continue;

                        strObjNameTemp = strTokens[0];

                        if (!ParseAnimationParms(strTokens[0])) continue;

                        switch (m_iCurrentObjectIndicator)
                        {
                            case CLoaderASE::eGeomObjectInd:
                                {
                                    if (m_pAnimObject != NULL)
                                    {
                                        m_pAnimObject->SetObjectType(CAnimObject::eGeomObject);
                                    }
                                    break;
                                }
                            case CLoaderASE::eLightObjectInd:
                                {
                                    if (m_pAnimObject != NULL)
                                    {
                                        iTemp = strTokens[0].find("RotCenter");
                                        if (!(iTemp != std::string::npos && strTokens[0].length() < 16))
                                        {
                                            m_pAnimObject->SetObjectType(CAnimObject::eLightObject);
                                        }
                                    }
                                    break;
                                }
                        }
                        break;
                    }
                case CLoaderASE::eMesh:
                    {
                        m_pMesh = new CMesh();
                        m_pMeshTVertList = new CMeshTVertList();
                        m_pMeshTFaceList = new CMeshTFaceList();
                        m_pMesh->SetMeshTVertList(m_pMeshTVertList);
                        m_pMesh->SetMeshTFaceList(m_pMeshTFaceList);
                        m_theStack.push('{');
                        break;
                    }
                case CLoaderASE::eMeshNumVertex:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pMesh->SetMeshNumVertex(iTemp);
                        break;
                    }
                case CLoaderASE::eMeshNumFaces:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pMesh->SetMeshNumFaces(iTemp);
                        break;
                    }
                case CLoaderASE::eMeshVertexList:
                    {
                        strTokens.clear();
                        iCount = m_pMesh->GetMeshNumVertex();

                        for (unsigned int i = 0; i < iCount; i++)
                        {
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens);
                            m_pMesh->InsertVertex(CVertex(atof(strTokens[3].c_str()), atof(strTokens[4].c_str()), atof(strTokens[2].c_str())));
                            strTokens.clear();
                        }
                        m_theStack.push('{');

                        if (m_iAnimationObjectIndicator == CLoaderASE::eAnimRotate ||
                            m_iAnimationObjectIndicator == CLoaderASE::eAnimTranslate)
                        {
                            m_pAnimObject->SetVertexList(m_pMesh->GetMeshVertexList());
                        }
                        break;
                    }
                case CLoaderASE::eMeshVertex:
                    {
                        break;
                    }
                case CLoaderASE::eMeshFaceList:
                    {
                        strTokens.clear();
                        iCount = m_pMesh->GetMeshNumFaces();

                        for (unsigned int i = 0; i < iCount; i++)
                        {
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens);
                            if (!atoi(strTokens[15].c_str()) && strTokens.size() < 18)
                                m_pMesh->InsertMeshFace(CMeshFace(atoi(strTokens[3].c_str()), atoi(strTokens[5].c_str()), atoi(strTokens[7].c_str()), atoi(strTokens[16].c_str()), 0, (bool)atoi(strTokens[9].c_str()), (bool)atoi(strTokens[11].c_str()), (bool)atoi(strTokens[13].c_str())));
                            else
                                m_pMesh->InsertMeshFace(CMeshFace(atoi(strTokens[3].c_str()), atoi(strTokens[5].c_str()), atoi(strTokens[7].c_str()), atoi(strTokens[17].c_str()), atoi(strTokens[15].c_str()), (bool)atoi(strTokens[9].c_str()), (bool)atoi(strTokens[11].c_str()), (bool)atoi(strTokens[13].c_str())));
                            strTokens.clear();
                        }
                        m_theStack.push('{');
                        break;
                    }
                case CLoaderASE::eMeshFace:
                    {
                        break;
                    }
                case CLoaderASE::eMeshNumTVertex:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);

                        m_pMeshTVertList->SetMeshNumVertex(iTemp);
                        break;
                    }
                case CLoaderASE::eMeshTVertList:
                    {
                        strTokens.clear();
                        iCount = m_pMeshTVertList->GetMeshNumVertex();

                        for (unsigned int i = 0; i < iCount; i++)
                        {
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens);
                            m_pMeshTVertList->InsertMeshTVert(CTextureUVW(atof(strTokens[2].c_str()), atof(strTokens[3].c_str()), atof(strTokens[4].c_str())));
                            strTokens.clear();
                        }
                        m_theStack.push('{');
                        break;
                    }
                case CLoaderASE::eMeshTVert:
                    {
                        break;
                    }
                case CLoaderASE::eMeshNumTVFaces:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);

                        m_pMeshTFaceList->SetMeshNumTVFaces(iTemp);
                        break;
                    }
                case CLoaderASE::eMeshTFaceList:
                    {
                        strTokens.clear();
                        iCount = m_pMeshTFaceList->GetMeshNumTVFaces();

                        for (unsigned int i = 0; i < iCount; i++)
                        {
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens);
                            m_pMeshTFaceList->InsertMeshTFace(CMeshTface(atoi(strTokens[2].c_str()), atoi(strTokens[3].c_str()), atoi(strTokens[4].c_str())));
                            strTokens.clear();
                        }
                        m_theStack.push('{');
                        break;
                    }
                case CLoaderASE::eMeshTFace:
                    {
                        break;
                    }
                case CLoaderASE::eMeshNumCVertex:
                    {
                        break;
                    }
                case CLoaderASE::eMeshNormals:
                    {
                        iCount = m_pMesh->GetMeshNumFaces();

                        for (unsigned int i = 0; i < iCount; i++)
                        {
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens1);
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens2);
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens3);
                            ReadLine(strLine);
                            ParseLine(const_cast<char *>(strLine.c_str()), "\t\n ", strTokens4);

                            m_pMesh->InsertFaceNormal(CMeshFaceNormal(atoi(strTokens1[1].c_str()), atof(strTokens1[3].c_str()), atof(strTokens1[4].c_str()), atof(strTokens1[2].c_str()),
                                                                        CMeshVertexNormal(atoi(strTokens2[1].c_str()), atof(strTokens2[3].c_str()), atof(strTokens2[4].c_str()), atof(strTokens2[2].c_str())),
                                                                        CMeshVertexNormal(atoi(strTokens3[1].c_str()), atof(strTokens3[3].c_str()), atof(strTokens3[4].c_str()), atof(strTokens3[2].c_str())),
                                                                        CMeshVertexNormal(atoi(strTokens4[1].c_str()), atof(strTokens4[3].c_str()), atof(strTokens4[4].c_str()), atof(strTokens4[2].c_str()))));
                            strTokens1.clear();
                            strTokens2.clear();
                            strTokens3.clear();
                            strTokens4.clear();
                        }
                        m_theStack.push('{');
                        break;
                    }
                case CLoaderASE::eMeshFaceNormal:
                    {
                        break;
                    }
                case CLoaderASE::eMeshVertexNormal:
                    {
                        break;
                    }
                case CLoaderASE::ePropCastShadow:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pGeomObject->SetPropCastShadow((bool)iTemp);
                        break;
                    }
                case CLoaderASE::ePropRecvShadow:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pGeomObject->SetPropRecvShadow((bool)iTemp);
                        break;
                    }
                case CLoaderASE::eMaterialRef:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pGeomObject->SetMaterialRef(iTemp);
                        break;
                    }

                ////////////////////////////////////////////////////////////////////////////////////
                // LightObject parsing

                case CLoaderASE::eLightObject:
                    {
                        m_pLightObject = new CLightObject();
                        m_theStack.push('{');
                        m_iCurrentObjectIndicator = CLoaderASE::eLightObjectInd;
                        break;
                    }
                case CLoaderASE::eLightNodeName:
                    {
                        break;
                    }
                case CLoaderASE::eTmPos:
                    {
                        if (m_iCurrentObjectIndicator == CLoaderASE::eLightObjectInd)
                        {
                            ::sscanf(strLine.c_str(), "%f%f%f", &fTemp1, &fTemp2, &fTemp3);
                            m_pLightObject->SetTmPos(CVertex(fTemp2, fTemp3, fTemp1));

                            if (m_iAnimationObjectIndicator == CLoaderASE::eAnimRotate ||
                                m_iAnimationObjectIndicator == CLoaderASE::eAnimTranslate)
                            {
                                m_pAnimObject->InsertVertex(m_pLightObject->GetTmPos());
                            }
                        }
                        break;
                    }
                case CLoaderASE::eLightShadowsOff:
                    {
                        m_pLightObject->SetLightShadows(0);
                        break;
                    }
                case CLoaderASE::eLightShadowsMapped:
                    {
                        m_pLightObject->SetLightShadows(1);
                        break;
                    }
                case CLoaderASE::eLightShadowsRaytraced:
                    {
                        m_pLightObject->SetLightShadows(2);
                        break;
                    }
                case CLoaderASE::eLightUseLight:
                    {
                        ::sscanf(strLine.c_str(), "%i", &iTemp);
                        m_pLightObject->SetLightUseLight((bool)iTemp);
                        break;
                    }
                case CLoaderASE::eLightColor:
                    {
                        ::sscanf(strLine.c_str(), "%f%f%f", &fTemp1, &fTemp2, &fTemp3);
                        m_pLightObject->SetLightColor(CColorRGB(fTemp1, fTemp2, fTemp3));
                        break;
                    }
                case CLoaderASE::eLightIntens:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);
                        m_pLightObject->SetLightIntens(fTemp);
                        break;
                    }
                case CLoaderASE::eLightAttnend:
                    {
                        ::sscanf(strLine.c_str(), "%f", &fTemp);
                        m_pLightObject->SetLightAttnend(fTemp);
                        break;
                    }

                ////////////////////////////////////////////////////////////////////////////////////
                // Additional opening brace parsing for not known tags
                // Closing brace parsing and object completion

                case CLoaderASE::eOpeningBrace:
                    {
                        switch (m_iCurrentObjectIndicator)
                        {
                            case CLoaderASE::eMaterialInd:
                            case CLoaderASE::eSubMaterialInd:
                            case CLoaderASE::eGeomObjectInd:
                            case CLoaderASE::eLightObjectInd:
                                {
                                    m_theStack.push('{');
                                    break;
                                }
                        }
                        break;
                    }
                case CLoaderASE::eClosingBrace:
                    {
                        switch (m_iCurrentObjectIndicator)
                        {
                            case CLoaderASE::eMaterialInd:
                                {
                                    cTemp = m_theStack.top();
                                    if (cTemp == '{')
                                    {
                                        m_theStack.pop();
                                        if (m_pMaterial->GetMapDiffuse() == NULL)
                                        {
                                            m_pMaterial->SetMapDiffuse(m_pMapDiffuse);
                                        }
                                        if (m_theStack.empty()) // empty stack means we completed the parsing for one complete object
                                        {
                                            m_iCurrentObjectIndicator = CLoaderASE::eUndefinedInd;
                                            m_pScene->InsertMaterial(m_pMaterial);
                                        }
                                    }
                                    break;
                                }
                            case CLoaderASE::eSubMaterialInd:
                                {
                                    cTemp = m_theStack.top();
                                    if (cTemp == '{')
                                    {
                                        m_theStack.pop();
                                        m_iCurrentObjectIndicator = CLoaderASE::eMaterialInd;
                                        m_pSubMaterial->SetMapDiffuse(m_pMapDiffuse);
                                        m_pMaterial->InsertSubMaterial(m_pSubMaterial);
                                    }
                                    break;
                                }
                            case CLoaderASE::eGeomObjectInd:
                                {
                                    cTemp = m_theStack.top();
                                    if (cTemp == '{')
                                    {
                                        m_theStack.pop();
                                        if (m_theStack.empty()) // empty stack means we completed the parsing for one complete object
                                        {
                                            m_iCurrentObjectIndicator = CLoaderASE::eUndefinedInd;
                                            m_pGeomObject->SetMesh(m_pMesh);
                                            m_pScene->InsertGeomObject(m_pGeomObject);

                                            // Insert and set this object as an animation object
                                            if (m_iAnimationObjectIndicator == CLoaderASE::eAnimRotate ||
                                                m_iAnimationObjectIndicator == CLoaderASE::eAnimTranslate)
                                            {
                                                if (m_pAnimObject->GetObjectType() == CAnimObject::eGeomObject)
                                                {
                                                    m_iAnimationObjectIndicator = CLoaderASE::eAnimUnknownTag;
                                                    m_pScene->InsertAnimObject(m_pAnimObject);
                                                    m_pAnimObject->SetSceneObjIndex(m_pScene->GetGeomObjects().size() - 1);
                                                    m_pGeomObject->SetAnimated(true);
                                                }
                                            }
                                        }
                                    }
                                    break;
                                }
                            case CLoaderASE::eLightObjectInd:
                                {
                                    cTemp = m_theStack.top();
                                    if (cTemp == '{')
                                    {
                                        m_theStack.pop();
                                        if (m_theStack.empty()) // empty stack means we completed the parsing for one complete object
                                        {
                                            m_iCurrentObjectIndicator = CLoaderASE::eUndefinedInd;
                                            m_pScene->InsertLightObject(m_pLightObject);

                                            // Insert and set this light as an animation object
                                            if (m_iAnimationObjectIndicator == CLoaderASE::eAnimRotate ||
                                                m_iAnimationObjectIndicator == CLoaderASE::eAnimTranslate)
                                            {
                                                if (m_pAnimObject->GetObjectType() == CAnimObject::eLightObject)
                                                {
                                                    m_iAnimationObjectIndicator = CLoaderASE::eAnimUnknownTag;
                                                    m_pScene->InsertAnimObject(m_pAnimObject);
                                                    m_pAnimObject->SetSceneObjIndex(m_pScene->GetLightObjects().size() - 1);
                                                }
                                            }
                                        }
                                    }
                                    break;
                                }
                        }
                        break;
                    }
            }
        }
    }
    catch(std::exception& e)
    {
        CApp::WriteToLog(e.what());
    }

	m_pScene->SetGeomObjectCount(m_pScene->GetGeomObjects().size());
	m_pScene->SetLightObjectCount(m_pScene->GetLightObjects().size());

	// setup the rotation centers for the animated objects
	for (unsigned int i = 0; i < m_pScene->GetLightObjectCount(); i++)
	{
		CLightObject * pLightObject = m_pScene->GetLightObject(i);

		for (unsigned int j = 0; j < m_pScene->GetAnimObjects().size(); j++)
		{
			CAnimObject * pAnimObject = m_pScene->GetAnimObjects()[j];

			if (pLightObject->GetNodeName() == pAnimObject->m_strRotCenter)
			{
				pAnimObject->m_RotCenter = pLightObject->GetTmPos();
			}
		}
	}
}
